/**
 * 
 */
package jnzb.model.binding;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

import jnzb.control.Configuration;
import jnzb.control.util.FileUtils;
import jnzb.model.nzb.File;
import jnzb.model.nzb.NZB;
import jnzb.model.nzb.Segment;
import nanoxml.XMLElement;
import nanoxml.XMLParseException;

/**
 * Rather than using an XML-Java binding utility, which would be easy, but way
 * over the top and have massive overheads, I'll manually do the binding using
 * the NanoXML-Lite XML parser.
 * 
 * This is a factory to get an NZB object from an NZB file
 * 
 * @author Rid
 * 
 */
public class NZBBinder {

	private NZBBinder() {
	}

	/**
	 * Get the NZB from the serialised progress file
	 * @param name
	 * @return
	 * @throws CorruptProgressException 
	 */
	public static NZB loadProgressFile(String name) throws CorruptProgressException {
		FileInputStream fis=null;;
		BufferedInputStream bis=null;
		ObjectInputStream ois=null;
		NZB nzb=null;
		try {
			fis = new FileInputStream(Configuration.getInstance().getNZBPath(name)
					+ name + ".progress");
			bis = new BufferedInputStream(fis);
			ois=new ObjectInputStream(bis);
			nzb = (NZB) ois.readObject();
			ois.close();
			bis.close();
			fis.close();
		} catch (FileNotFoundException e) {
			nzb=null;
		} catch (IOException e) {
			throw new CorruptProgressException("Progress for " + name + " is corrupt", e);
		} catch (ClassNotFoundException e) {
			nzb=null;
		} finally {
			FileUtils.close(ois);
			FileUtils.close(bis);
			FileUtils.close(fis);
		}
		return nzb;
	}

	@SuppressWarnings("unchecked")
	public static NZB loadFromFile(String filename) throws XMLParseException,
			IOException {

		// Constructor should call this!
		NZB nzb = new NZB();

		java.io.File meta = new java.io.File(filename);
		nzb.setFilename(meta.getName());
		nzb.setPath(meta.getPath());
		meta=null;
		
		XMLElement nzbFile = new XMLElement();
		Reader r = new FileReader(filename);
		nzbFile.parseFromReader(r);

		// Enumerate over all file elements
		Enumeration<XMLElement> fileElements = nzbFile.enumerateChildren();
		List<File> files = new ArrayList<File>();

		while (fileElements.hasMoreElements()) {
			XMLElement fileElement = fileElements.nextElement();
			if (fileElement.getName().equalsIgnoreCase("file")) {
				if (fileElement.countChildren() == 2) {
					File file = new File(nzb);
					file.setPoster(fileElement.getStringAttribute("poster"));
					file.setDate(fileElement.getIntAttribute("date"));
					file.setSubject(fileElement.getStringAttribute("subject"));

					// Get groups
					file.setGroups(processGroups(getChild(fileElement,
									"groups")));
					// Get segments
					file.setSegments(processSegments(getChild(fileElement,
							"segments")));

					files.add(file);
				} else {
					throw new XMLParseException(
							"Expected 2 elements per file element", null);
				}
			} else {
				throw new XMLParseException("Expected file element", null);
			}
		}
		nzb.setFiles(files);
		r.close();
		return nzb;
	}

	@SuppressWarnings("unchecked")
	private static List<String> processGroups(XMLElement groups) {
		List<String> allGroups = new ArrayList<String>();

		Enumeration<XMLElement> groupList = groups.enumerateChildren();
		while (groupList.hasMoreElements()) {
			XMLElement group = groupList.nextElement();
			allGroups.add(group.getContent());
		}
		return allGroups;
	}

	@SuppressWarnings("unchecked")
	private static List<Segment> processSegments(XMLElement segments) {
		List<Segment> allSegments = new ArrayList<Segment>();
		
		// Enumerate over all segment elements
		Enumeration<XMLElement> allSegment = segments.enumerateChildren();
		while (allSegment.hasMoreElements()){
			XMLElement segmentElement = allSegment.nextElement();
			Segment segment = new Segment();
			
			segment.setBytes(segmentElement.getIntAttribute("bytes"));
			segment.setNumber(segmentElement.getIntAttribute("number"));
			segment.setMessageID(segmentElement.getContent());
			
			allSegments.add(segment);
		}
		// Will sort into segment order
		Collections.sort(allSegments);
		
		return allSegments;
	}

	@SuppressWarnings("unchecked")
	private static XMLElement getChild(XMLElement element, String child) {
		Enumeration<XMLElement> children = element.enumerateChildren();
		while (children.hasMoreElements()) {
			XMLElement kid = children.nextElement();
			if (kid.getName().equals(child)) {
				return kid;
			}
		}
		return null;
	}
}
